﻿using AutoMapper;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using StarsAdmin.Application.File.Dtos;
using StarsAdmin.Application.LoginLog.Dtos;
using StarsAdmin.Core.Cache;
using StarsAdmin.Core.Consts;
using StarsAdmin.Core.DB.Entities;
using StarsAdmin.Core.Exceptions;
using StarsAdmin.Core.Extensions;
using StarsAdmin.Core.Helpers;
using StarsAdmin.Core.Models;
using StarsAdmin.Repository.File;

namespace StarsAdmin.Application.File.Services
{
    public class FileService : IFileService
    {
        private readonly IMapper _mapper;
        private readonly ICacheService _cache;
        private readonly IFileRepository _fileRepository;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IWebHostEnvironment _webHostEnvironment;

        public FileService(IMapper mapper, ICacheService cacheService, IHttpContextAccessor httpContextAccessor, IWebHostEnvironment webHostEnvironment, IFileRepository fileRepository)
        {
            _mapper = mapper;
            _cache = cacheService;
            _fileRepository = fileRepository;
            _httpContextAccessor = httpContextAccessor;
            _webHostEnvironment = webHostEnvironment;
        }

        #region 查询

        public async Task<List<FileDto>> GetAllAsync(BaseQueryInput input)
        {
            var result = await _fileRepository.GetAll().AsNoTracking()
                .WhereIf(input != null && input.QueryConditions.Any(), input.QueryConditions)
                .OrderByIf(input != null && input.Sort != null, input.Sort, input.Order != false)
                .ToListAsync();
            return _mapper.Map<List<FileDto>>(result);
        }

        public async Task<Page<FileDto>> GetPageAsync(PageQueryInput input)
        {
            var result = await _fileRepository.GetAll().AsNoTracking()
                        .WhereIf(input != null && input.QueryConditions.Any(), input.QueryConditions)
                        .OrderByIf(input != null && input.Sort != null, input.Sort, input.Order != false)
                        .ToPageListAsync(input.CurrentPage, input.PageSize);
            return _mapper.Map<Page<FileDto>>(result);
        }

        public async Task<FileDto?> GetByIdAsync(long id)
        {
            var result = await _fileRepository.GetByIdAsync(id);
            return _mapper.Map<FileDto>(result);
        }

        public async Task<List<FileDto>> GetByIdsAsync(long[] ids)
        {
            var result = await _fileRepository.GetByIds(ids).ToListAsync();
            return _mapper.Map<List<FileDto>>(result);
        }

        #endregion 查询

        #region 上传下载

        public async Task<FileDto> UploadFileAsync(IFormFile file, string fileDirectory = "upload")
        {
            var md5Files = _fileRepository.Get();
            var (entity, isAdd) = await GetFileEntity(file, md5Files, fileDirectory);
            if (isAdd)
            {
                await _fileRepository.AddAndSaveAsync(entity);
            } else
            {
                await _fileRepository.UpdateAndSaveAsync(entity);
            }
            return _mapper.Map<FileDto>(entity);
        }

        public async Task<List<FileDto>> UploadFilesAsync(IFormFileCollection files, string fileDirectory = "upload")
        {
            var addFiles = new List<FileEntity>();
            var updateFiles = new List<FileEntity>();
            var md5Files = _fileRepository.Get();
            foreach (var item in files)
            {
                var (file, isAdd) = await GetFileEntity(item, md5Files, fileDirectory);
                if (isAdd)
                {
                    addFiles.Add(file);
                } else
                {
                    updateFiles.Add(file);
                }
            }
            if (addFiles.Any())
            {
                await _fileRepository.AddRangeAsync(addFiles);
            }
            if (updateFiles.Any())
            {
                await _fileRepository.UpdateRangeAsync(updateFiles);
            }
            await _fileRepository.SaveChangesAsync();

            return _mapper.Map<List<FileDto>>(addFiles.Concat(updateFiles));
        }

        public async Task<FileStreamResult?> DownloadFileAsync(DownloadFileInput input)
        {
            if (input.Id != null && input.Id != 0)
            {
                var file = await _fileRepository.GetByIdAsync((long)input.Id);
                if (file == null)
                {
                    throw new NotFoundException("未找到指定的文件。");
                }
                return file.FileDirectory.GetFileStreamResult(_webHostEnvironment.WebRootPath, file.Extension, file.FileName);
            } else if (input.Ids != null && input.Ids.Length > 0)
            {
                var files = await _fileRepository.GetByIds(input.Ids).ToListAsync();
                if (files == null || files.Count < 1)
                {
                    throw new NotFoundException("未找到指定的文件。");
                }
                return files.GetZipFileStreamResult(_webHostEnvironment.WebRootPath);
            } else if (!string.IsNullOrWhiteSpace(input.Url))
            {
                var file = await _fileRepository.GetBy(e => e.Url == input.Url).FirstOrDefaultAsync();
                if (file == null)
                {
                    throw new NotFoundException("未找到指定的文件。");
                }
                return file.FileDirectory.GetFileStreamResult(_webHostEnvironment.WebRootPath, file.Extension, file.FileName);
            } else if (input.Urls != null && input.Urls.Length > 0)
            {
                var files = await _fileRepository.GetBy(e => input.Urls.Contains(e.Url)).ToListAsync();
                if (files == null || files.Count < 1)
                {
                    throw new NotFoundException("未找到指定的文件。");
                }
                return files.GetZipFileStreamResult(_webHostEnvironment.WebRootPath);
            } else
            {
                throw new NotFoundException("至少应该有一个字段不能为默认值。");
            }
        }

        #endregion 上传下载

        #region 删除

        public Task<bool> DeleteAsync(long id)
        {
            return _fileRepository.DeleteAndSaveAsync(id);
        }

        public Task<bool> BatchDeleteAsync(long[] ids)
        {
            return _fileRepository.DeleteRangeAndSaveAsync(ids);
        }

        #endregion 删除

        #region 其他

        /// <summary>
        /// 获取文件信息
        /// </summary>
        /// <param name="file"></param>
        /// <param name="md5Files"></param>
        /// <param name="fileDirectory"></param>
        /// <returns>新增返回true，存在返回false</returns>
        private async Task<(FileEntity, bool)> GetFileEntity(IFormFile file, IQueryable<FileEntity> md5Files, string fileDirectory = "upload")
        {
            var extention = Path.GetExtension(file.FileName).ToLower();
            var md5 = MD5Helper.GetHash(file.OpenReadStream());
            var md5FileEntity = await md5Files.Where(e => e.Md5 == md5).FirstOrDefaultAsync();
            if (md5FileEntity != null)
            {
                md5FileEntity.FileName = Path.GetFileNameWithoutExtension(file.FileName);
                md5FileEntity.SaveFileName = md5;
                md5FileEntity.Extension = extention;
                md5FileEntity.Type = extention.GetFileContentType();
                md5FileEntity.IsDeleted = false;
                return (md5FileEntity, false);
            }
            var fileLenth = file.Length;
            var filePath = fileDirectory.CombineAndFormatPath(md5 + extention);
            var localPath = _webHostEnvironment.WebRootPath.CombineAndFormatPath(filePath);
            var fileEntity = new FileEntity
            {
                FileName = Path.GetFileNameWithoutExtension(file.FileName),
                SaveFileName = md5,
                Extension = extention,
                Type = extention.GetFileContentType(),
                Size = fileLenth,
                FileDirectory = filePath,
                SizeFormat = fileLenth.FormatFileSize(),
                Md5 = md5,
                Url = _httpContextAccessor.GetUrl(filePath),
            };
            await file.SaveAsync(localPath);
            return (fileEntity, true);
        }

        #endregion 其他
    }
}